package com.yoo.module_base.utils

import android.content.ContentResolver
import android.content.ContentUris
import android.content.Context
import android.graphics.BitmapFactory
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.os.storage.StorageManager
import android.provider.DocumentsContract
import android.provider.MediaStore
import android.text.TextUtils
import android.util.Log
import androidx.core.content.FileProvider
import java.io.*
import java.lang.reflect.Array

object FileUtils {
    /**
     * 复制文件
     *
     * @param oldFile String
     * @param newFile String
     * @param isImage boolean
     */
    fun copyFile(context: Context?, oldFile: String?, newFile: String?, isImage: Boolean): String? {
        var fis: FileInputStream? = null
        var fos: FileOutputStream? = null
        try {
            fis = FileInputStream(oldFile)
            fos = FileOutputStream(newFile)
            val buf = ByteArray(1024)
            var by = 0
            while (fis.read(buf).also { by = it } != -1) {
                fos.write(buf, 0, by)
            }
            if (isImage) {
                sendImageChangeBroadcast(context, newFile)
            }
            fis.close()
            fos.close()
            return File(newFile).absolutePath
        } catch (e: FileNotFoundException) {
            e.printStackTrace()
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return null
    }

    /**
     * 更新系统图库
     *
     * @param imgPath
     */
    fun sendImageChangeBroadcast(context: Context?, imgPath: String?) {
        if (TextUtils.isEmpty(imgPath)) return
        val file = File(imgPath)
        if (file.exists() && file.isFile) {
            MediaScannerConnection.scanFile(
                context,
                arrayOf(file.absolutePath),
                null,
                null
            )
        }
    }

    /**
     * 批量保存图片到相册
     * @param imageList 图片数组集合
     */
    fun saveImageToAlbum(context: Context?, imageList: ArrayList<String?>): String? {
        for (imgPath in imageList) {
            return copyFile(context, imgPath, getCameraPath() + System.nanoTime() + ".png", true)
        }
        return null
    }

    /**
     * 获取手机相册路径
     *
     * @return
     */
    private fun getCameraPath(): String {
        val cameraPath = Environment.getExternalStorageDirectory().absolutePath + "/DCIM/Camera/"
        val file = File(cameraPath)
        if (!file.exists()) {
            file.mkdirs()
        }
        return cameraPath
    }

    fun getAppCachePath(context: Context?): String {
        val cachePath =
            Environment.getExternalStorageDirectory().absolutePath + "/Android/data/" + context?.applicationContext?.packageName + "/cache/"
        val file = File(cachePath)
        if (!file.exists()) {
            file.mkdirs()
        }
        return cachePath
    }

    fun deleteSingleFile(fileName: String): Boolean {
        try {
            if (TextUtils.isEmpty(fileName)) {
                return true
            }
            val file = File(fileName)
            // 如果文件路径所对应的文件存在，并且是一个文件，则直接删除
            return if (file.exists() && file.isFile) {
                if (file.delete()) {
                    LogUtil.infoLog("删除单个文件" + fileName + "成功！")
                    true
                } else {
                    LogUtil.infoLog("删除单个文件" + fileName + "失败！")
                    false
                }
            } else {
                LogUtil.infoLog("删除单个文件失败：" + fileName + "不存在！")
                false
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return false

    }

    /**
     * Uri to file.
     *
     * @param uri The uri.
     * @return file
     */
    fun uri2File(context: Context, uri: Uri): File? {
        Log.d("UriUtils", uri.toString())
        val authority = uri.authority
        val scheme = uri.scheme
        val path = uri.path
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
            && path != null
        ) {
            val externals = arrayOf("/external", "/external_path")
            for (external: String in externals) {
                if (path.startsWith("$external/")) {
                    val file = File(
                        Environment.getExternalStorageDirectory().absolutePath
                                + path.replace(external, "")
                    )
                    if (file.exists()) {
                        Log.d("UriUtils", "$uri -> $external")
                        return file
                    }
                }
            }
        }
        if ((ContentResolver.SCHEME_FILE == scheme)) {
            if (path != null) return File(path)
            Log.d("UriUtils", "$uri parse failed. -> 0")
            return null
        } // end 0
        else if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
                    && DocumentsContract.isDocumentUri(context, uri))
        ) {
            if (("com.android.externalstorage.documents" == authority)) {
                val docId = DocumentsContract.getDocumentId(uri)
                val split = docId.split(":").toTypedArray()
                val type = split[0]
                if ("primary".equals(type, ignoreCase = true)) {
                    return File(
                        Environment.getExternalStorageDirectory().toString() + "/" + split[1]
                    )
                } else {
                    // Below logic is how External Storage provider build URI for documents
                    // http://stackoverflow.com/questions/28605278/android-5-sd-card-label
                    val mStorageManager =
                        context.getSystemService(Context.STORAGE_SERVICE) as StorageManager
                    try {
                        val storageVolumeClazz = Class.forName("android.os.storage.StorageVolume")
                        val getVolumeList = mStorageManager.javaClass.getMethod("getVolumeList")
                        val getUuid = storageVolumeClazz.getMethod("getUuid")
                        val getState = storageVolumeClazz.getMethod("getState")
                        val getPath = storageVolumeClazz.getMethod("getPath")
                        val isPrimary = storageVolumeClazz.getMethod("isPrimary")
                        val isEmulated = storageVolumeClazz.getMethod("isEmulated")
                        val result = getVolumeList.invoke(mStorageManager)
                        val length = Array.getLength(result)
                        for (i in 0 until length) {
                            val storageVolumeElement = Array.get(result, i)
                            //String uuid = (String) getUuid.invoke(storageVolumeElement);
                            val mounted =
                                ((Environment.MEDIA_MOUNTED == getState.invoke(storageVolumeElement)) || (Environment.MEDIA_MOUNTED_READ_ONLY == getState.invoke(
                                    storageVolumeElement
                                )))

                            //if the media is not mounted, we need not get the volume details
                            if (!mounted) continue

                            //Primary storage is already handled.
                            if ((isPrimary.invoke(storageVolumeElement) as Boolean
                                        && isEmulated.invoke(storageVolumeElement) as Boolean)
                            ) {
                                continue
                            }
                            val uuid: String? = getUuid.invoke(storageVolumeElement) as String
                            if (uuid != null && (uuid == type)) {
                                return File(
                                    getPath.invoke(storageVolumeElement).toString() + "/" + split[1]
                                )
                            }
                        }
                    } catch (ex: java.lang.Exception) {
                        Log.d("UriUtils", "$uri parse failed. $ex -> 1_0")
                    }
                }
                Log.d("UriUtils", "$uri parse failed. -> 1_0")
                return null
            } // end 1_0
            else if (("com.android.providers.downloads.documents" == authority)) {
                val id = DocumentsContract.getDocumentId(uri)
                if (!TextUtils.isEmpty(id)) {
                    try {
                        val contentUri = ContentUris.withAppendedId(
                            Uri.parse("content://downloads/public_downloads"),
                            java.lang.Long.valueOf(id)
                        )
                        return FileUtils.getFileFromUri(context, contentUri, "1_1")
                    } catch (e: NumberFormatException) {
                        if (id.startsWith("raw:")) {
                            return File(id.substring(4))
                        }
                    }
                }
                Log.d("UriUtils", "$uri parse failed. -> 1_1")
                return null
            } // end 1_1
            else if (("com.android.providers.media.documents" == authority)) {
                val docId = DocumentsContract.getDocumentId(uri)
                val split = docId.split(":").toTypedArray()
                val type = split[0]
                val contentUri: Uri
                if (("image" == type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
                } else if (("video" == type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
                } else if (("audio" == type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
                } else {
                    Log.d("UriUtils", "$uri parse failed. -> 1_2")
                    return null
                }
                val selection = "_id=?"
                val selectionArgs = arrayOf(split[1])
                return FileUtils.getFileFromUri(
                    context,
                    contentUri,
                    selection,
                    selectionArgs,
                    "1_2"
                )
            } // end 1_2
            else if ((ContentResolver.SCHEME_CONTENT == scheme)) {
                return FileUtils.getFileFromUri(context, uri, "1_3")
            } // end 1_3
            else {
                Log.d("UriUtils", "$uri parse failed. -> 1_4")
                return null
            } // end 1_4
        } // end 1
        else if ((ContentResolver.SCHEME_CONTENT == scheme)) {
            if (FileUtils.isGooglePhotosUri(uri)) return File(uri.lastPathSegment)
            if (FileUtils.isQQMediaDocument(uri)) {
                val fileDir = Environment.getExternalStorageDirectory()
                return File(fileDir, path!!.substring("/QQBrowser".length))
            }
            return FileUtils.getFileFromUri(context, uri, "2")
        } // end 2
        else {
            Log.d("UriUtils", "$uri parse failed. -> 3")
            return null
        } // end 3
    }

    private fun getFileFromUri(context: Context, uri: Uri, code: String): File? {
        return getFileFromUri(context, uri, null, null, code)
    }

    private fun getFileFromUri(
        context: Context, uri: Uri,
        selection: String?,
        selectionArgs: kotlin.Array<String>?,
        code: String
    ): File? {
        val cursor = context.contentResolver.query(
            uri, arrayOf("_data"), selection, selectionArgs, null
        )
        if (cursor == null) {
            Log.d("UriUtils", "$uri parse failed(cursor is null). -> $code")
            return null
        }
        return try {
            if (cursor.moveToFirst()) {
                val columnIndex = cursor.getColumnIndex("_data")
                if (columnIndex > -1) {
                    File(cursor.getString(columnIndex))
                } else {
                    Log.d(
                        "UriUtils",
                        "$uri parse failed(columnIndex: $columnIndex is wrong). -> $code"
                    )
                    null
                }
            } else {
                Log.d("UriUtils", "$uri parse failed(moveToFirst return false). -> $code")
                null
            }
        } catch (e: java.lang.Exception) {
            Log.d("UriUtils", "$uri parse failed. -> $code")
            null
        } finally {
            cursor.close()
        }
    }

    private fun isGooglePhotosUri(uri: Uri): Boolean {
        return "com.google.android.apps.photos.content" == uri.authority
    }

    private fun isQQMediaDocument(uri: Uri): Boolean {
        return "com.tencent.mtt.fileprovider" == uri.authority
    }
    fun isImageFile(filePath: String?): Boolean {
        val options = BitmapFactory.Options()
        options.inJustDecodeBounds = true
        BitmapFactory.decodeFile(filePath, options)
        return options.outWidth != -1
    }

    /**
     * File to uri.
     *
     * @param file The file.
     * @return uri
     */
    fun file2Uri(context: Context, file: File): Uri? {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            val authority = context.packageName + ".provider"
            FileProvider.getUriForFile(context, authority, file)
        } else {
            Uri.fromFile(file)
        }
    }
    fun uri2Path(context: Context?, uri: Uri?): String? {
        if (uri == null) return null
        val file = uri2File(context!!, uri) ?: return null
        return file.absolutePath
    }
}